﻿/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using Velocity4Net.Runtime.ResourceManage;
using Velocity4Net.Errors;
using Velocity4Net.Runtime.Parse;
using Velocity4Net.App.Events;
using Velocity4Net.Util.Introspection;
using Velocity4Net.Runtime.Logging;
using Velocity4Net.Runtime.Parse.AST;
using Velocity4Net.Runtime.Directives;
using Velocity4Net.Ctx;
using Velocity4Net.Runtime.ResourceManage.Loader;
using Velocity4Net.Cfg;
using System.Configuration;

namespace Velocity4Net.Runtime
{

    /**
     * This is the Runtime system for Velocity. It is the
     * single access point for all functionality in Velocity.
     * It adheres to the mediator pattern and is the only
     * structure that developers need to be familiar with
     * in order to get Velocity to perform.
     *
     * The Runtime will also cooperate with external
     * systems like Turbine. Runtime properties can
     * set and then the Runtime is initialized.
     *
     * Turbine, for example, knows where the templates
     * are to be loaded from, and where the Velocity
     * log file should be placed.
     *
     * So in the case of Velocity cooperating with Turbine
     * the code might look something like the following:
     *
     * <blockquote><code><pre>
     * ri.setProperty(Runtime.FILE_RESOURCE_LOADER_PATH, templatePath);
     * ri.setProperty(Runtime.RUNTIME_LOG, pathToVelocityLog);
     * ri.init();
     * </pre></code></blockquote>
     *
     * <pre>
     * -----------------------------------------------------------------------
     * N O T E S  O N  R U N T I M E  I N I T I A L I Z A T I O N
     * -----------------------------------------------------------------------
     * init()
     *
     * If init() is called by itself the RuntimeInstance will initialize
     * with a set of default values.
     * -----------------------------------------------------------------------
     * init(String/Properties)
     *
     * In this case the default velocity properties are layed down
     * first to provide a solid base, then any properties provided
     * in the given properties object will override the corresponding
     * default property.
     * -----------------------------------------------------------------------
     * </pre>
     *
     * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
     * @author <a href="mailto:jlb@houseofdistraction.com">Jeff Bowden</a>
     * @author <a href="mailto:geirm@optonline.net">Geir Magusson Jr.</a>
     * @version $Id: RuntimeInstance.java 898050 2010-01-11 20:15:31Z nbubna $
     */
    public class RuntimeInstance : RuntimeConstants, RuntimeServices
    {
        /**
         *  VelocimacroFactory object to manage VMs
         */
        private VelocimacroFactory vmFactory = null;

        /**
         * The Runtime logger.  We start with an instance of
         * a 'primordial logger', which just collects log messages
         * then, when the log system is initialized, all the
         * messages get dumpted out of the primordial one into the real one.
         */
        private Log log = new Log();

        /**
         * The Runtime parser pool
         */
        private ParserPool parserPool;

        /**
         * Indicate whether the Runtime is in the midst of initialization.
         */
        private bool initializing = false;

        /**
         * Indicate whether the Runtime has been fully initialized.
         */
        private bool initialized = false;

        /**
         * These are the properties that are laid down over top
         * of the default properties when requested.
         */
        private VelocitySettings overridingProperties = null;

        /**
         * This is a hashtable of initialized directives.
         * The directives that populate this hashtable are
         * taken from the RUNTIME_DEFAULT_DIRECTIVES
         * property file. 
         */
        private Hashtable runtimeDirectives = new Hashtable();
        /**
         * Copy of the actual runtimeDirectives that is shared between
         * parsers. Whenever directives are updated, the synchronized 
         * runtimeDirectives is first updated and then an unsynchronized
         * copy of it is passed to parsers.
         */
        private Hashtable runtimeDirectivesShared;

        /**
         * Object that houses the configuration options for
         * the velocity runtime. The ExtendedProperties object allows
         * the convenient retrieval of a subset of properties.
         * For example all the properties for a resource loader
         * can be retrieved from the main ExtendedProperties object
         * using something like the following:
         *
         * ExtendedProperties loaderConfiguration =
         *         configuration.subset(loaderID);
         *
         * And a configuration is a lot more convenient to deal
         * with then conventional properties objects, or Maps.
         */
        private VelocitySettings configuration = new VelocitySettings();

        private ResourceManager resourceManager = null;

        /**
         * This stores the engine-wide set of event handlers.  Event handlers for
         * each specific merge are stored in the context.
         */
        private EventCartridge eventCartridge = null;

        /*
         *  Each runtime instance has it's own introspector
         *  to ensure that each instance is completely separate.
         */
        private Introspector introspector = null;

        /*
         * Settings for provision of root scope for evaluate(...) calls.
         */
        private String evaluateScopeName = "evaluate";
        private bool provideEvaluateScope = false;

        /*
         *  Opaque reference to something specificed by the
         *  application for use in application supplied/specified
         *  pluggable components
         */
        private Hashtable applicationAttributes = null;
        private Uberspect uberSpect;
        private String encoding;

        /**
         * Creates a new RuntimeInstance object.
         */
        public RuntimeInstance()
        {
            /*
             *  create a VM factory, introspector, and application attributes
             */
            vmFactory = new VelocimacroFactory(this);

            /*
             *  make a new introspector and initialize it
             */
            introspector = new Introspector(getLog());

            /*
             * and a store for the application attributes
             */
            applicationAttributes = new Hashtable();
        }

        /**
         * This is the primary initialization method in the Velocity
         * Runtime. The systems that are setup/initialized here are
         * as follows:
         *
         * <ul>
         *   <li>Logging System</li>
         *   <li>ResourceManager</li>
         *   <li>EventHandler</li>
         *   <li>Parser Pool</li>
         *   <li>Global Cache</li>
         *   <li>Static Content Include System</li>
         *   <li>Velocimacro System</li>
         * </ul>
         */
        public void Init()
        {
            lock (this)
            {
                if (!initialized && !initializing)
                {
                    log.debug("Initializing Velocity, Calling init()...");
                    initializing = true;

                    log.trace("*******************************************************************");
                    log.debug("Starting Apache Velocity v@build.version@ (compiled: @build.time@)");
                    log.trace("RuntimeInstance initializing.");

                    InitializeProperties();
                    InitializeLog();
                    InitializeResourceManager();
                    InitializeDirectives();
                    InitializeEventHandlers();
                    initializeParserPool();

                    initializeIntrospection();
                    initializeEvaluateScopeSettings();
                    /*
                     *  initialize the VM Factory.  It will use the properties
                     * accessable from Runtime, so keep this here at the end.
                     */
                    vmFactory.InitVelocimacro();

                    log.trace("RuntimeInstance successfully initialized.");

                    initialized = true;
                    initializing = false;
                }
            }
        }

        /**
         * Returns true if the RuntimeInstance has been successfully initialized.
         * @return True if the RuntimeInstance has been successfully initialized.
         * @since 1.5
         */
        public bool Initialized
        {
            get
            {
                return initialized;
            }
        }

        /**
         * Init or die! (with some log help, of course)
         */
        private void RequireInitialization()
        {
            if (!initialized)
            {
                try
                {
                    Init();
                }
                catch (System.Exception e)
                {
                    getLog().error("Could not auto-initialize Velocity", e);
                    throw new ApplicationException("Velocity could not be initialized!", e);
                }
            }
        }

        /**
         *  Gets the classname for the Uberspect introspection package and
         *  instantiates an instance.
         */
        private void initializeIntrospection()
        {
            String[] uberspectors = configuration.GetStringArray(RuntimeConstants.UBERSPECT_CLASSNAME);
            for (int i = 0; i < uberspectors.Length; i++)
            {
                String rm = uberspectors[i];
                Object o = null;

                try
                {
                    o = Activator.CreateInstance(Type.GetType(rm));
                }
                catch (TypeLoadException cnfe)
                {
                    String err = "The specified class for Uberspect (" + rm
                        + ") does not exist or is not accessible to the current classloader.";
                    log.error(err);
                    throw new VelocityException(err, cnfe);
                }
                catch (MethodInvocationException ie)
                {
                    throw new VelocityException("Could not instantiate class '" + rm + "'", ie);
                }
                catch (MethodAccessException ae)
                {
                    throw new VelocityException("Cannot access class '" + rm + "'", ae);
                }

                if (!(o is Uberspect))
                {
                    String err = "The specified class for Uberspect ("
                        + rm + ") does not implement " + typeof(Uberspect).Name
                        + "; Velocity is not initialized correctly.";

                    log.error(err);
                    throw new VelocityException(err);
                }

                Uberspect u = (Uberspect)o;

                if (u is UberspectLoggable)
                {
                    ((UberspectLoggable)u).Log = getLog();
                }

                //if (u is RuntimeServicesAware)
                //{
                //    ((RuntimeServicesAware)u).setRuntimeServices(this);
                //}

                if (uberSpect == null)
                {
                    uberSpect = u;
                }
                //else
                //{
                //    if (u is ChainableUberspector)
                //    {
                //        ((ChainableUberspector)u).wrap(uberSpect);
                //        uberSpect = u;
                //    }
                //    else
                //    {
                //        uberSpect = new LinkingUberspector(uberSpect,u);
                //    }
                //}
            }
            if (uberSpect != null)
            {
                uberSpect.init();
            }
            else
            {
                /*
                 *  someone screwed up.  Lets not fool around...
                 */

                String err = "It appears that no class was specified as the"
                + " Uberspect.  Please ensure that all configuration"
                + " information is correct.";

                log.error(err);
                throw new VelocityException(err);
            }
        }


        /**
         * Initializes the Velocity Runtime with properties file.
         * The properties file may be in the file system proper,
         * or the properties file may be in the classpath.
         */
        private void SetDefaultProperties()
        {
            configuration.AddProperty("runtime.log.logsystem.class", "Velocity4Net.Runtime.Logging.Log4JLogChute,Velocity4Net");
            configuration.AddProperty("runtime.log", "velocity4net.log");

            configuration.AddProperty("runtime.log.error.stacktrace", "false");
            configuration.AddProperty("runtime.log.warn.stacktrace", "false");
            configuration.AddProperty("runtime.log.info.stacktrace", "false");
            configuration.AddProperty("runtime.log.invalid.reference", "true");

            //configuration.AddProperty("runtime.log.logsystem.log4net.pattern","%d - %m%n");
            //configuration.AddProperty("runtime.log.logsystem.log4net.file.size","100000");
            //configuration.AddProperty("runtime.log.logsystem.log4net.file.backups","1");
            //configuration.AddProperty("runtime.log.logsystem.log4net.syslogd.host","my.syslog.server.com");
            //configuration.AddProperty("runtime.log.logsystem.log4net.syslogd.facility","LOG_DAEMON");
            //configuration.AddProperty("runtime.log.logsystem.log4net.remote.host","my.remote.server.com");
            //configuration.AddProperty("runtime.log.logsystem.log4net.remote.port","1099");
            //configuration.AddProperty("runtime.log.logsystem.log4net.email.server","localhost");
            //configuration.AddProperty("runtime.log.logsystem.log4net.email.from","root@localhost");
            //configuration.AddProperty("runtime.log.logsystem.log4net.email.to","root@localhost");
            //configuration.AddProperty("runtime.log.logsystem.log4net.email.subject","NVelocity Error Report");
            //configuration.AddProperty("runtime.log.logsystem.log4net.email.buffer.size","512");

            configuration.AddProperty("input.encoding", "UTF-8");
            configuration.AddProperty("output.encoding", "UTF-8");

            configuration.AddProperty("directive.include.output.errormsg.start", "<!-- include error :");
            configuration.AddProperty("directive.include.output.errormsg.end", "see error log -->");

            configuration.AddProperty("directive.parse.max.depth", "10");

            configuration.AddProperty(RuntimeConstants.RESOURCE_LOADER, "file");

            configuration.AddProperty("file.resource.loader.description", "Velocity4Net File Resource Loader");
            configuration.AddProperty("file.resource.loader.class", "Velocity4Net.Runtime.ResourceManage.Loader.FileResourceLoader,Velocity4Net");
            configuration.AddProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, ".");
            configuration.AddProperty("file.resource.loader.cache", "true");
            configuration.AddProperty("file.resource.loader.modificationCheckInterval", "2");
            //configuration.AddProperty("velocimacro.library "," VM_global_library.vm");

            configuration.AddProperty("velocimacro.permissions.allow.inline", "true");
            configuration.AddProperty("velocimacro.permissions.allow.inline.to.replace.global", "false");
            configuration.AddProperty("velocimacro.permissions.allow.inline.local.scope", "false");

            configuration.AddProperty("velocimacro.context.localscope", "false");

            configuration.AddProperty("runtime.interpolate.string.literals", "true");


            configuration.AddProperty(RuntimeConstants.RESOURCE_MANAGER_CLASS, "Velocity4Net.Runtime.ResourceManage.ResourceManagerImpl,Velocity4Net");
            configuration.AddProperty(RuntimeConstants.RESOURCE_MANAGER_CACHE_CLASS, "Velocity4Net.Runtime.ResourceManage.ResourceCacheImpl,Velocity4Net");

            configuration.AddProperty(RuntimeConstants.COUNTER_NAME, "velocityCount");
            configuration.AddProperty("directive.foreach.counter.initial.value", "1");
            configuration.AddProperty("directive.foreach.maxloops", "-1");

            configuration.AddProperty("directive.foreach.iterator.name", "velocityHasNext");

            configuration.AddProperty("foreach.provide.scope.control", "true");

            configuration.AddProperty("runtime.introspector.uberspect", "Velocity4Net.Util.Introspection.UberspectImpl,Velocity4Net");
            configuration.AddProperty("parser.pool.class", "Velocity4Net.Runtime.ParserPoolImpl,Velocity4Net");

            VelocityConfigurationSection section = (VelocityConfigurationSection)ConfigurationManager.GetSection("velocity");

            foreach (string s in section.Settings.AllKeys)
            {
                configuration.AddProperty(s, section.Settings[s]);
            }
        }

        /**
         * Allows an external system to set a property in
         * the Velocity Runtime.
         *
         * @param key property key
         * @param  value property value
         */
        public void SetProperty(String key, Object value)
        {
            if (overridingProperties == null)
            {
                overridingProperties = new VelocitySettings();
            }

            overridingProperties.SetProperty(key, value);
        }


        /**
         * Add all the properties in props to the RuntimeInstance properties
         */
        public void SetProperties(Dictionary<string, object> props)
        {
            foreach (string s in props.Keys)
            {
                SetProperty(s, props[s]);
            }
        }

        /**
         * Add a property to the configuration. If it already
         * exists then the value stated here will be added
         * to the configuration entry. For example, if
         *
         * resource.loader = file
         *
         * is already present in the configuration and you
         *
         * addProperty("resource.loader", "classpath")
         *
         * Then you will end up with a Vector like the
         * following:
         *
         * ["file", "classpath"]
         *
         * @param  key
         * @param  value
         */
        public void AddProperty(String key, Object value)
        {
            if (overridingProperties == null)
            {
                overridingProperties = new VelocitySettings();
            }

            overridingProperties.AddProperty(key, value);
        }

        /**
         * Clear the values pertaining to a particular
         * property.
         *
         * @param key of property to clear
         */
        public void clearProperty(String key)
        {
            if (overridingProperties != null)
            {
                overridingProperties.ClearProperty(key);
            }
        }

        /**
         *  Allows an external caller to get a property.  The calling
         *  routine is required to know the type, as this routine
         *  will return an Object, as that is what properties can be.
         *
         *  @param key property to return
         *  @return Value of the property or null if it does not exist.
         */
        public Object getProperty(String key)
        {
            Object o = null;

            /**
             * Before initialization, check the user-entered properties first.
             */
            if (!initialized && overridingProperties != null)
            {
                o = overridingProperties[key];
            }

            /**
             * After initialization, configuration will hold all properties.
             */
            if (o == null)
            {
                o = configuration.GetProperty(key);
            }
            if (o is String)
            {
                return o;
            }
            else
            {
                return o;
            }
        }

        /**
         * Initialize Velocity properties, if the default
         * properties have not been laid down first then
         * do so. Then proceed to process any overriding
         * properties. Laying down the default properties
         * gives a much greater chance of having a
         * working system.
         */
        private void InitializeProperties()
        {
            /*
             * Always lay down the default properties first as
             * to provide a solid base.
             */
            if (!configuration.isInitialized)
            {
                SetDefaultProperties();
            }

            if (overridingProperties != null)
            {
                configuration.Combine(overridingProperties);
            }
        }

        /**
         * Initialize the Velocity Runtime with a Properties
         * object.
         *
         * @param p Velocity properties for initialization
         */
        public void Init(Dictionary<string, object> p)
        {
            LoadFromAppConfig();
            SetProperties(p);
            Init();
        }

        private void LoadFromAppConfig()
        {
            try
            {
                VelocityConfigurationSection config = (VelocityConfigurationSection)ConfigurationManager.GetSection("velocity");
                foreach (string key in config.Settings.AllKeys)
                {
                    AddProperty(key, config.Settings[key].Value);
                }
            }
            catch (ConfigurationErrorsException e)
            {
                log.error("read config failed:" + e.Message);
            }
        }

        private void InitializeResourceManager()
        {
            /*
            * Which resource manager?
            */

            System.String rm = GetString(RuntimeConstants.RESOURCE_MANAGER_CLASS);

            if (rm != null && rm.Length > 0)
            {
                /*
                *  if something was specified, then make one.
                *  if that isn't a ResourceManager, consider
                *  this a huge error and throw
                */

                System.Object o = null;

                //UPGRADE_NOTE: Exception 'java.lang.ClassNotFoundException' was converted to ' ' which has different behavior. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1100"'
                try
                {
                    //UPGRADE_TODO: Format of parameters of method 'java.lang.Class.forName' are different in the equivalent in .NET. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1092"'
                    Type rmType = System.Type.GetType(rm);
                    o = System.Activator.CreateInstance(rmType);
                }
                catch (System.Exception cnfe)
                {
                    System.String err = "The specified class for Resourcemanager (" + rm + ") does not exist (or is not accessible to the current classlaoder.";
                    error(err);
                    throw new System.Exception(err);
                }

                if (!(o is ResourceManager))
                {
                    System.String err = "The specified class for ResourceManager (" + rm + ") does not implement org.apache.runtime.resource.ResourceManager." + " Velocity not initialized correctly.";

                    error(err);
                    throw new System.Exception(err);
                }

                resourceManager = (ResourceManager)o;

                resourceManager.Initialize(this);
            }
            else
            {
                /*
                        *  someone screwed up.  Lets not fool around...
                        */

                System.String err = "It appears that no class was specified as the" + " ResourceManager.  Please ensure that all configuration" + " information is correct.";

                error(err);
                throw new System.Exception(err);
            }
        }

        private void InitializeEventHandlers()
        {

            eventCartridge = new EventCartridge();

            /**
             * For each type of event handler, get the class name, instantiate it, and store it.
             */

            String[] referenceinsertion = configuration.GetStringArray(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION);
            if (referenceinsertion != null)
            {
                for (int i = 0; i < referenceinsertion.Length; i++)
                {
                    Velocity4Net.App.Events.EventHandler ev = initializeSpecificEventHandler(referenceinsertion[i], RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION, typeof(ReferenceInsertionEventHandler));
                    if (ev != null)
                        eventCartridge.addReferenceInsertionEventHandler((ReferenceInsertionEventHandler)ev);
                }
            }

            String[] nullset = configuration.GetStringArray(RuntimeConstants.EVENTHANDLER_NULLSET);
            if (nullset != null)
            {
                for (int i = 0; i < nullset.Length; i++)
                {
                    Velocity4Net.App.Events.EventHandler ev = initializeSpecificEventHandler(nullset[i], RuntimeConstants.EVENTHANDLER_NULLSET, typeof(NullSetEventHandler));
                    if (ev != null)
                        eventCartridge.addNullSetEventHandler((NullSetEventHandler)ev);
                }
            }

            String[] methodexception = configuration.GetStringArray(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION);
            if (methodexception != null)
            {
                for (int i = 0; i < methodexception.Length; i++)
                {
                    Velocity4Net.App.Events.EventHandler ev = initializeSpecificEventHandler(methodexception[i], RuntimeConstants.EVENTHANDLER_METHODEXCEPTION, typeof(MethodExceptionEventHandler));
                    if (ev != null)
                        eventCartridge.addMethodExceptionHandler((MethodExceptionEventHandler)ev);
                }
            }

            String[] includeHandler = configuration.GetStringArray(RuntimeConstants.EVENTHANDLER_INCLUDE);
            if (includeHandler != null)
            {
                for (int i = 0; i < includeHandler.Length; i++)
                {
                    Velocity4Net.App.Events.EventHandler ev = initializeSpecificEventHandler(includeHandler[i], RuntimeConstants.EVENTHANDLER_INCLUDE, typeof(IncludeEventHandler));
                    if (ev != null)
                        eventCartridge.addIncludeEventHandler((IncludeEventHandler)ev);
                }
            }

            String[] invalidReferenceSet = configuration.GetStringArray(RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES);
            if (invalidReferenceSet != null)
            {
                for (int i = 0; i < invalidReferenceSet.Length; i++)
                {
                    Velocity4Net.App.Events.EventHandler ev = initializeSpecificEventHandler(invalidReferenceSet[i], RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES, typeof(InvalidReferenceEventHandler));
                    if (ev != null)
                    {
                        eventCartridge.addInvalidReferenceEventHandler((InvalidReferenceEventHandler)ev);
                    }
                }
            }


        }


        private Velocity4Net.App.Events.EventHandler initializeSpecificEventHandler(String classname, String paramName, Type EventHandlerInterface)
        {
            if (classname != null && classname.Length > 0)
            {
                Object o = null;
                try
                {
                    o = System.Activator.CreateInstance(Type.GetType(classname));
                }
                catch (TypeLoadException cnfe)
                {
                    String err = "The specified class for "
                        + paramName + " (" + classname
                        + ") does not exist or is not accessible to the current classloader.";
                    log.error(err);
                    throw new VelocityException(err, cnfe);
                }
                catch (TypeInitializationException ie)
                {
                    throw new VelocityException("Could not instantiate class '" + classname + "'", ie);
                }
                catch (MemberAccessException ae)
                {
                    throw new VelocityException("Cannot access class '" + classname + "'", ae);
                }

                if (!EventHandlerInterface.IsAssignableFrom(EventHandlerInterface))
                {
                    String err = "The specified class for " + paramName + " ("
                        + classname + ") does not implement "
                        + EventHandlerInterface.Name
                        + "; Velocity is not initialized correctly.";

                    log.error(err);
                    throw new VelocityException(err);
                }

                Velocity4Net.App.Events.EventHandler ev = (Velocity4Net.App.Events.EventHandler)o;
                //if ( ev is RuntimeServicesAware )
                //    ((RuntimeServicesAware) ev).setRuntimeServices(this);
                return ev;

            }
            else
                return null;
        }

        /**
         * Initialize the Velocity logging system.
         */
        private void InitializeLog()
        {
            // since the Log we started with was just placeholding,
            // let's update it with the real LogChute settings.
            try
            {
                LogManager.UpdateLog(this.log, this);
            }
            catch (System.Exception e)
            {
                throw new VelocityException("Error initializing log: " + e.Message, e);
            }
        }


        /**
         * This methods initializes all the directives
         * that are used by the Velocity Runtime. The
         * directives to be initialized are listed in
         * the RUNTIME_DEFAULT_DIRECTIVES properties
         * file.
         */
        private void InitializeDirectives()
        {
            /*
            * Initialize the runtime directive table.
            * This will be used for creating parsers.
            */
            runtimeDirectives = new System.Collections.Hashtable();

            VelocitySettings directiveProperties = new VelocitySettings();

            try
            {

                directiveProperties.AddProperty("directive.1", "Velocity4Net.Runtime.Directives.Foreach,Velocity4Net");
                directiveProperties.AddProperty("directive.2", "Velocity4Net.Runtime.Directives.Include,Velocity4Net");
                directiveProperties.AddProperty("directive.3", "Velocity4Net.Runtime.Directives.Parse,Velocity4Net");
                directiveProperties.AddProperty("directive.4", "Velocity4Net.Runtime.Directives.Macro,Velocity4Net");
                directiveProperties.AddProperty("directive.5", "Velocity4Net.Runtime.Directives.Evaluate,Velocity4Net");
                directiveProperties.AddProperty("directive.6", "Velocity4Net.Runtime.Directives.Break,Velocity4Net");
                directiveProperties.AddProperty("directive.7", "Velocity4Net.Runtime.Directives.Define,Velocity4Net");
                directiveProperties.AddProperty("directive.8", "Velocity4Net.Runtime.Directives.Stop,Velocity4Net");


                VelocityConfigurationSection c = (VelocityConfigurationSection)ConfigurationManager.GetSection("velocity");
                foreach (string s in c.Directives)
                {
                    directiveProperties.SetProperty(s, c.Directives[s]);
                }
            }
            catch (Exception ex)
            {
                throw new Exception("Error loading directive.properties! " + "Something is very wrong if these properties " + "aren't being located. Either your Velocity " + "distribution is incomplete or your Velocity " + "jar file is corrupted!\n" + ex.Message);
            }


            /*
            * Grab all the values of the properties. These
            * are all class names for example:
            *
            * Velocity4Net.Runtime.Directive.Foreach
            */
            ICollection directiveClasses = directiveProperties.Values;

            //UPGRADE_TODO: method 'java.util.Enumeration.hasMoreElements' was converted to ' ' which has a different behavior. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1073_javautilEnumerationhasMoreElements"'
            foreach (string directiveClass in directiveClasses)
            {
                //UPGRADE_TODO: method 'java.util.Enumeration.nextElement' was converted to ' ' which has a different behavior. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1073_javautilEnumerationnextElement"'
                loadDirective(directiveClass);
            }

            /*
            *  now the user's directives
            */
            System.String[] userdirective = configuration.GetStringArray("userdirective");
            for (int i = 0; i < userdirective.Length; i++)
            {
                loadDirective(userdirective[i]);
            }
        }

        /**
         * Programatically add a directive.
         * @param directive
         */
        public void addDirective(Directive directive)
        {
            lock (this)
            {
                runtimeDirectives.Add(directive.Name, directive);
                UpdateSharedDirectivesMap();
            }
        }

        /**
         * Retrieve a previously instantiated directive.
         * @param name name of the directive
         * @return the {@link Directive} for that name
         */
        public Directive getDirective(String name)
        {
            return (Directive)runtimeDirectivesShared[name];
        }

        /**
         * Remove a directive.
         * @param name name of the directive.
         */
        public void removeDirective(String name)
        {
            lock (this)
            {
                runtimeDirectives.Remove(name);
                UpdateSharedDirectivesMap();
            }
        }

        /**
         * Makes an unsynchronized copy of the directives map
         * that is used for Directive lookups by all parsers.
         * 
         * This follows Copy-on-Write pattern. The cost of creating
         * a new map is acceptable since directives are typically
         * set and modified only during Velocity setup phase.
         */
        private void UpdateSharedDirectivesMap()
        {
            Hashtable tmp = new Hashtable(runtimeDirectives);
            runtimeDirectivesShared = tmp;
        }

        /**
         *  instantiates and loads the directive with some basic checks
         *
         *  @param directiveClass classname of directive to load
         */
        public void loadDirective(System.String directiveClass)
        {
            try
            {
                Object o = System.Activator.CreateInstance(Type.GetType(directiveClass));

                if (o is Directive)
                {
                    Directive directive = (Directive)o;
                    addDirective(directive);
                }
                else
                {
                    String msg = directiveClass + " does not implement "
                        + typeof(Directive).Name + "; it cannot be loaded.";
                    log.error(msg);
                    throw new VelocityException(msg);
                }
            }
            // The ugly threesome:  ClassNotFoundException,
            // IllegalAccessException, InstantiationException.
            // Ignore Findbugs complaint for now.
            catch (System.Exception e)
            {
                String msg = "Failed to load Directive: " + directiveClass;
                log.error(msg, e);
                throw new VelocityException(msg, e);
            }
        }


        /**
         * Initializes the Velocity parser pool.
         */
        private void initializeParserPool()
        {
            /*
             * Which parser pool?
             */
            String pp = GetString(RuntimeConstants.PARSER_POOL_CLASS);

            if (pp != null && pp.Length > 0)
            {
                /*
                 *  if something was specified, then make one.
                 *  if that isn't a ParserPool, consider
                 *  this a huge error and throw
                 */

                Object o = null;

                try
                {
                    o = Activator.CreateInstance(Type.GetType(pp));
                }
                catch (TypeLoadException cnfe)
                {
                    String err = "The specified class for ParserPool ("
                        + pp
                        + ") does not exist (or is not accessible to the current classloader.";
                    log.error(err);
                    throw new VelocityException(err, cnfe);
                }
                catch (TypeInitializationException ie)
                {
                    throw new VelocityException("Could not instantiate class '" + pp + "'", ie);
                }
                catch (InvalidOperationException ae)
                {
                    throw new VelocityException("Cannot access class '" + pp + "'", ae);
                }

                if (!(o is ParserPool))
                {
                    String err = "The specified class for ParserPool ("
                        + pp + ") does not implement " + typeof(ParserPool)
                        + " Velocity not initialized correctly.";

                    log.error(err);
                    throw new VelocityException(err);
                }

                parserPool = (ParserPool)o;

                parserPool.initialize(this);
            }
            else
            {
                /*
                 *  someone screwed up.  Lets not fool around...
                 */

                String err = "It appears that no class was specified as the"
                    + " ParserPool.  Please ensure that all configuration"
                    + " information is correct.";

                log.error(err);
                throw new VelocityException(err);
            }

        }

        /**
         * Returns a JavaCC generated Parser.
         *
         * @return Parser javacc generated parser
         */
        public Parser createNewParser()
        {
            RequireInitialization();

            Parser parser = new Parser(this);
            return parser;
        }

        /**
         * Parse the input and return the root of
         * AST node structure.
         * <br><br>
         *  In the event that it runs out of parsers in the
         *  pool, it will create and let them be GC'd
         *  dynamically, logging that it has to do that.  This
         *  is considered an exceptional condition.  It is
         *  expected that the user will set the
         *  PARSER_POOL_SIZE property appropriately for their
         *  application.  We will revisit this.
         *
         * @param string String to be parsed
         * @param templateName name of the template being parsed
         * @return A root node representing the template as an AST tree.
         * @throws ParseException When the string could not be parsed as a template.
         * @since 1.6
         */
        public SimpleNode Parse(String str, String templateName)
        {
            return Parse(new StringReader(str), templateName);
        }

        /**
         * Parse the input and return the root of
         * AST node structure.
         * <br><br>
         *  In the event that it runs out of parsers in the
         *  pool, it will create and let them be GC'd
         *  dynamically, logging that it has to do that.  This
         *  is considered an exceptional condition.  It is
         *  expected that the user will set the
         *  PARSER_POOL_SIZE property appropriately for their
         *  application.  We will revisit this.
         *
         * @param reader Reader retrieved by a resource loader
         * @param templateName name of the template being parsed
         * @return A root node representing the template as an AST tree.
         * @throws ParseException When the template could not be parsed.
         */
        public SimpleNode Parse(TextReader reader, String templateName)
        {
            /*
             *  do it and dump the VM namespace for this template
             */
            return Parse(reader, templateName, true);
        }

        /**
         *  Parse the input and return the root of the AST node structure.
         *
         * @param reader Reader retrieved by a resource loader
         * @param templateName name of the template being parsed
         * @param dumpNamespace flag to dump the Velocimacro namespace for this template
         * @return A root node representing the template as an AST tree.
         * @throws ParseException When the template could not be parsed.
         */
        public SimpleNode Parse(TextReader reader, String templateName, bool dumpNamespace)
        {
            RequireInitialization();

            Parser parser = (Parser)parserPool.get();
            bool keepParser = true;
            if (parser == null)
            {
                /*
                 *  if we couldn't get a parser from the pool make one and log it.
                 */
                if (log.isInfoEnabled())
                {
                    log.info("Runtime : ran out of parsers. Creating a new one. "
                          + " Please increment the parser.pool.size property."
                          + " The current value is too small.");
                }
                parser = createNewParser();
                keepParser = false;
            }

            try
            {
                /*
                 *  dump namespace if we are told to.  Generally, you want to
                 *  do this - you don't in special circumstances, such as
                 *  when a VM is getting init()-ed & parsed
                 */
                if (dumpNamespace)
                {
                    dumpVMNamespace(templateName);
                }
                return parser.Parse(reader, templateName);
            }
            finally
            {
                if (keepParser)
                {
                    parserPool.add(parser);
                }

            }
        }

        private void initializeEvaluateScopeSettings()
        {
            String property = evaluateScopeName + '.' + PROVIDE_SCOPE_CONTROL;
            provideEvaluateScope = GetBoolean(property, provideEvaluateScope);
        }

        /**
         * Renders the input string using the context into the output writer.
         * To be used when a template is dynamically constructed, or want to use
         * Velocity as a token replacer.
         *
         * @param context context to use in rendering input string
         * @param out  Writer in which to render the output
         * @param logTag  string to be used as the template name for log
         *                messages in case of error
         * @param instring input string containing the VTL to be rendered
         *
         * @return true if successful, false otherwise.  If false, see
         *              Velocity runtime log
         * @throws ParseErrorException The template could not be parsed.
         * @throws MethodInvocationException A method on a context object could not be invoked.
         * @throws ResourceNotFoundException A referenced resource could not be loaded.
         * @since Velocity 1.6
         */
        public bool Evaluate(IContext context, TextWriter _out,
                                String logTag, String instring)
        {
            return Evaluate(context, _out, logTag, new StringReader(instring));
        }

        /**
         * Renders the input reader using the context into the output writer.
         * To be used when a template is dynamically constructed, or want to
         * use Velocity as a token replacer.
         *
         * @param context context to use in rendering input string
         * @param writer  Writer in which to render the output
         * @param logTag  string to be used as the template name for log messages
         *                in case of error
         * @param reader Reader containing the VTL to be rendered
         *
         * @return true if successful, false otherwise.  If false, see
         *              Velocity runtime log
         * @throws ParseErrorException The template could not be parsed.
         * @throws MethodInvocationException A method on a context object could not be invoked.
         * @throws ResourceNotFoundException A referenced resource could not be loaded.
         * @since Velocity 1.6
         */
        public bool Evaluate(IContext context, TextWriter writer,
                                String logTag, TextReader reader)
        {
            if (logTag == null)
            {
                throw new NullReferenceException("logTag (i.e. template name) cannot be null, you must provide an identifier for the content being evaluated");
            }

            SimpleNode nodeTree = null;
            try
            {
                nodeTree = Parse(reader, logTag);
            }
            catch (ParseException pex)
            {
                throw new ParseErrorException(pex, null);
            }
            catch (TemplateInitException pex)
            {
                throw new ParseErrorException(pex, null);
            }

            if (nodeTree == null)
            {
                return false;
            }
            else
            {
                return Render(context, writer, logTag, nodeTree);
            }
        }


        /**
         * Initializes and renders the AST {@link SimpleNode} using the context
         * into the output writer.
         *
         * @param context context to use in rendering input string
         * @param writer  Writer in which to render the output
         * @param logTag  string to be used as the template name for log messages
         *                in case of error
         * @param nodeTree SimpleNode which is the root of the AST to be rendered
         *
         * @return true if successful, false otherwise.  If false, see
         *              Velocity runtime log for errors
         * @throws ParseErrorException The template could not be parsed.
         * @throws MethodInvocationException A method on a context object could not be invoked.
         * @throws ResourceNotFoundException A referenced resource could not be loaded.
         * @since Velocity 1.6
         */
        public bool Render(IContext context, TextWriter writer,
                              String logTag, SimpleNode nodeTree)
        {
            /*
             * we want to init then render
             */
            InternalContextAdapterImpl ica =
                new InternalContextAdapterImpl(context);

            ica.PushCurrentTemplateName(logTag);

            try
            {
                try
                {
                    nodeTree.Init(ica, this);
                }
                catch (TemplateInitException pex)
                {
                    throw new ParseErrorException(pex, null);
                }
                /**
                 * pass through application level runtime exceptions
                 */
                catch (ApplicationException e)
                {
                    throw e;
                }
                catch (System.Exception e)
                {
                    String msg = "RuntimeInstance.render(): init exception for tag = " + logTag;
                    getLog().error(msg, e);
                    throw new VelocityException(msg, e);
                }

                try
                {
                    if (provideEvaluateScope)
                    {
                        Object previous = ica.Get(evaluateScopeName);
                        context.Put(evaluateScopeName, new Scope(this, previous));
                    }
                    nodeTree.Render(ica, writer);
                }
                catch (StopCommand stop)
                {
                    if (!stop.isFor(this))
                    {
                        throw stop;
                    }
                    else if (getLog().isDebugEnabled())
                    {
                        getLog().debug(stop.Message);
                    }
                }
                catch (IOException e)
                {
                    throw new VelocityException("IO Error in writer: " + e.Message, e);
                }
            }
            finally
            {
                ica.PopCurrentTemplateName();
                if (provideEvaluateScope)
                {
                    Object obj = ica.Get(evaluateScopeName);
                    if (obj is Scope)
                    {
                        Scope scope = (Scope)obj;
                        if (scope.Parent != null)
                        {
                            ica.Put(evaluateScopeName, scope.Parent);
                        }
                        else if (scope.Replaced != null)
                        {
                            ica.Put(evaluateScopeName, scope.Replaced);
                        }
                        else
                        {
                            ica.Remove(evaluateScopeName);
                        }
                    }
                }
            }

            return true;
        }

        /**
         * Invokes a currently registered Velocimacro with the params provided
         * and places the rendered stream into the writer.
         * <br>
         * Note : currently only accepts args to the VM if they are in the context.
         *
         * @param vmName name of Velocimacro to call
         * @param logTag string to be used for template name in case of error. if null,
         *               the vmName will be used
         * @param params keys for args used to invoke Velocimacro, in java format
         *               rather than VTL (eg  "foo" or "bar" rather than "$foo" or "$bar")
         * @param context Context object containing data/objects used for rendering.
         * @param writer  Writer for output stream
         * @return true if Velocimacro exists and successfully invoked, false otherwise.
         * @since 1.6
         */
        public bool InvokeVelocimacro(String vmName, String logTag,
                                         String[] _params, IContext context,
                                        TextWriter writer)
        {
            /* check necessary parameters */
            if (vmName == null || context == null || writer == null)
            {
                String msg = "RuntimeInstance.invokeVelocimacro() : invalid call : vmName, context, and writer must not be null";
                getLog().error(msg);
                throw new NullReferenceException(msg);
            }

            /* handle easily corrected parameters */
            if (logTag == null)
            {
                logTag = vmName;
            }
            if (_params == null)
            {
                _params = new String[0];
            }

            /* does the VM exist? */
            if (!isVelocimacro(vmName, logTag))
            {
                String msg = "RuntimeInstance.invokeVelocimacro() : VM '" + vmName
                             + "' is not registered.";
                getLog().error(msg);
                throw new VelocityException(msg);
            }

            /* now just create the VM call, and use evaluate */
            StringBuilder template = new StringBuilder("#");
            template.Append(vmName);
            template.Append("(");
            for (int i = 0; i < _params.Length; i++)
            {
                template.Append(" $");
                template.Append(_params[i]);
            }
            template.Append(" )");

            return Evaluate(context, writer, logTag, template.ToString());
        }

        /**
         * Retrieves and caches the configured default encoding
         * for better performance. (VELOCITY-606)
         */
        private String DefaultEncoding
        {
            get
            {
                if (encoding == null)
                {
                    encoding = GetString(INPUT_ENCODING, ENCODING_DEFAULT);
                }
                return encoding;
            }
        }

        /**
         * Returns a <code>Template</code> from the resource manager.
         * This method assumes that the character encoding of the
         * template is set by the <code>input.encoding</code>
         * property.  The default is "ISO-8859-1"
         *
         * @param name The file name of the desired template.
         * @return     The template.
         * @throws ResourceNotFoundException if template not found
         *          from any available source.
         * @throws ParseErrorException if template cannot be parsed due
         *          to syntax (or other) error.
         */
        public Template getTemplate(String name)
        {
            return GetTemplate(name, DefaultEncoding);
        }

        /**
         * Returns a <code>Template</code> from the resource manager
         *
         * @param name The  name of the desired template.
         * @param encoding Character encoding of the template
         * @return     The template.
         * @throws ResourceNotFoundException if template not found
         *          from any available source.
         * @throws ParseErrorException if template cannot be parsed due
         *          to syntax (or other) error.
         */
        public Template GetTemplate(String name, String encoding)
        {
            RequireInitialization();

            return (Template)
                    resourceManager.GetResource(name,
                        ResourceManager_Fields.RESOURCE_TEMPLATE, encoding);
        }

        /**
         * Returns a static content resource from the
         * resource manager.  Uses the current value
         * if INPUT_ENCODING as the character encoding.
         *
         * @param name Name of content resource to get
         * @return parsed ContentResource object ready for use
         * @throws ResourceNotFoundException if template not found
         *          from any available source.
         * @throws ParseErrorException When the template could not be parsed.
         */
        public ContentResource getContent(String name)
        {
            /*
             *  the encoding is irrelvant as we don't do any converstion
             *  the bytestream should be dumped to the output stream
             */

            return getContent(name, DefaultEncoding);
        }

        /**
         * Returns a static content resource from the
         * resource manager.
         *
         * @param name Name of content resource to get
         * @param encoding Character encoding to use
         * @return parsed ContentResource object ready for use
         * @throws ResourceNotFoundException if template not found
         *          from any available source.
         * @throws ParseErrorException When the template could not be parsed.
         */
        public ContentResource getContent(String name, String encoding)
        {
            RequireInitialization();

            return (ContentResource)
                    resourceManager.GetResource(name,
                            ResourceManager_Fields.RESOURCE_CONTENT, encoding);
        }


        /**
         *  Determines if a template exists and returns name of the loader that
         *  provides it.  This is a slightly less hokey way to support
         *  the Velocity.resourceExists() utility method, which was broken
         *  when per-template encoding was introduced.  We can revisit this.
         *
         *  @param resourceName Name of template or content resource
         *  @return class name of loader than can provide it
         */
        public String getLoaderNameForResource(String resourceName)
        {
            RequireInitialization();

            return resourceManager.GetLoaderNameForResource(resourceName);
        }

        /**
         * Returns a convenient Log instance that wraps the current LogChute.
         * Use this to log error messages. It has the usual methods.
         *
         * @return A convenience Log instance that wraps the current LogChute.
         * @since 1.5
         */
        public Log getLog()
        {
            return log;
        }

        /**
         * @deprecated Use getLog() and call warn() on it.
         * @see Log#warn(Object)
         * @param message The message to logf.
         */
        public void warn(Object message)
        {
            getLog().warn(message);
        }

        /**
         * @deprecated Use getLog() and call info() on it.
         * @see Log#info(Object)
         * @param message The message to logf.
         */
        public void info(Object message)
        {
            getLog().info(message);
        }

        /**
         * @deprecated Use getLog() and call error() on it.
         * @see Log#error(Object)
         * @param message The message to logf.
         */
        public void error(Object message)
        {
            getLog().error(message);
        }

        /**
         * @deprecated Use getLog() and call debug() on it.
         * @see Log#debug(Object)
         * @param message The message to logf.
         */
        public void debug(Object message)
        {
            getLog().debug(message);
        }

        /**
         * String property accessor method with default to hide the
         * configuration implementation.
         *
         * @param key property key
         * @param defaultValue  default value to return if key not
         *               found in resource manager.
         * @return value of key or default
         */
        public String GetString(String key, String defaultValue)
        {
            return configuration.GetString(key, defaultValue);
        }

        /**
         * Returns the appropriate VelocimacroProxy object if vmName
         * is a valid current Velocimacro.
         *
         * @param vmName Name of velocimacro requested
         * @param templateName Name of the template that contains the velocimacro.
         * @return The requested VelocimacroProxy.
         * @since 1.6
         */
        public Directive GetVelocimacro(String vmName, String templateName)
        {
            return vmFactory.getVelocimacro(vmName, templateName);
        }

        /**
         * Returns the appropriate VelocimacroProxy object if vmName
         * is a valid current Velocimacro.
         *
         * @param vmName  Name of velocimacro requested
         * @param templateName Name of the namespace.
         * @param renderingTemplate Name of the template we are currently rendering. This
         *    information is needed when VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL setting is true
         *    and template contains a macro with the same name as the global macro library.
         * 
         * @since Velocity 1.6
         * 
         * @return VelocimacroProxy
         */
        public Directive GetVelocimacro(String vmName, String templateName, String renderingTemplate)
        {
            return vmFactory.getVelocimacro(vmName, templateName, renderingTemplate);
        }


        /**
         * Adds a new Velocimacro. Usually called by Macro only while parsing.
         *
         * @param name Name of velocimacro
         * @param macro String form of macro body
         * @param argArray Array of strings, containing the
         *                         #macro() arguments.  the 0th is the name.
         * @param sourceTemplate Name of the template that contains the velocimacro.
         * 
         * @deprecated Use addVelocimacro(String, Node, String[], String) instead
         * 
         * @return True if added, false if rejected for some
         *                  reason (either parameters or permission settings)
         */
        public bool AddVelocimacro(String name,
                                              String macro,
                                              String[] argArray,
                                              String sourceTemplate)
        {
            return vmFactory.addVelocimacro(name, macro, argArray, sourceTemplate);
        }

        /**
         * Adds a new Velocimacro. Usually called by Macro only while parsing.
         * 
         * Called by org.apache.velocity.runtime.directive.processAndRegister
         *
         * @param name  Name of velocimacro
         * @param macro  root AST node of the parsed macro
         * @param argArray  Array of strings, containing the
         *                         #macro() arguments.  the 0th is the name.
         * @param sourceTemplate
         * 
         * @since Velocity 1.6
         *                   
         * @return bool  True if added, false if rejected for some
         *                  reason (either parameters or permission settings)
         */
        public bool AddVelocimacro(String name,
                                              INode macro,
                                              String[] argArray,
                                              String sourceTemplate)
        {
            return vmFactory.addVelocimacro(name, macro, argArray, sourceTemplate);
        }


        /**
         *  Checks to see if a VM exists
         *
         * @param vmName Name of the Velocimacro.
         * @param templateName Template on which to look for the Macro.
         * @return True if VM by that name exists, false if not
         */
        public bool isVelocimacro(String vmName, String templateName)
        {
            return vmFactory.isVelocimacro(vmName, templateName);
        }

        /**
         * tells the vmFactory to dump the specified namespace.  This is to support
         * clearing the VM list when in inline-VM-local-scope mode
         * @param namespace Namespace to dump.
         * @return True if namespace was dumped successfully.
         */
        public bool dumpVMNamespace(String ns)
        {
            return vmFactory.dumpVMNamespace(ns);
        }

        /* --------------------------------------------------------------------
         * R U N T I M E  A C C E S S O R  M E T H O D S
         * --------------------------------------------------------------------
         * These are the getXXX() methods that are a simple wrapper
         * around the configuration object. This is an attempt
         * to make a the Velocity Runtime the single access point
         * for all things Velocity, and allow the Runtime to
         * adhere as closely as possible the the Mediator pattern
         * which is the ultimate goal.
         * --------------------------------------------------------------------
         */

        /**
         * String property accessor method to hide the configuration implementation
         * @param key  property key
         * @return   value of key or null
         */
        public String GetString(String key)
        {
            return configuration.GetString(key);
        }

        /**
         * Int property accessor method to hide the configuration implementation.
         *
         * @param key Property key
         * @return value
         */
        public int GetInt(String key)
        {
            return configuration.GetInt(key);
        }

        /**
         * Int property accessor method to hide the configuration implementation.
         *
         * @param key  property key
         * @param defaultValue The default value.
         * @return value
         */
        public int GetInt(String key, int defaultValue)
        {
            return configuration.GetInt(key, defaultValue);
        }

        /**
         * Boolean property accessor method to hide the configuration implementation.
         *
         * @param key property key
         * @param def The default value if property not found.
         * @return value of key or default value
         */
        public bool GetBoolean(String key, bool def)
        {
            return configuration.GetBoolean(key, def);
        }

        /**
         * Return the velocity runtime configuration object.
         *
         * @return Configuration object which houses the Velocity runtime
         * properties.
         */
        public VelocitySettings Configuration
        {
            get
            {
                return configuration;
            }
            set
            {
                if (overridingProperties == null)
                {
                    overridingProperties = configuration;
                }
                else
                {
                    // Avoid possible ConcurrentModificationException
                    if (overridingProperties != configuration)
                    {
                        overridingProperties.Combine(configuration);
                    }
                }
            }
        }

        /**
         *  Return the Introspector for this instance
         * @return The Introspector for this instance
         */
        public Introspector Introspector
        {
            get
            {
                return introspector;
            }
        }

        /**
         * Returns the event handlers for the application.
         * @return The event handlers for the application.
         * @since 1.5
         */
        public EventCartridge getApplicationEventCartridge()
        {
            return eventCartridge;
        }


        /**
         *  Gets the application attribute for the given key
         *
         * @param key
         * @return The application attribute for the given key.
         */
        public Object getApplicationAttribute(Object key)
        {
            return applicationAttributes[key];
        }

        /**
         *   Sets the application attribute for the given key
         *
         * @param key
         * @param o The new application attribute.
         * @return The old value of this attribute or null if it hasn't been set before.
         */
        public Object SetApplicationAttribute(Object key, Object o)
        {
            applicationAttributes.Add(key, o);
            return o;
        }

        /**
         * Returns the Uberspect object for this Instance.
         *
         * @return The Uberspect object for this Instance.
         */
        public Uberspect getUberspect()
        {
            return uberSpect;
        }

    }

}
